package com.funcourse.demo.util;
import java.util.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.KeyFactory;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class RsaHelper {
  private static final Logger logger = LoggerFactory.getLogger(RsaHelper.class);
  private RSAPublicKey publicKey;
  private RSAPrivateCrtKey privateKey;

//  static {
//    Security.addProvider(new BouncyCastleProvider()); //使用bouncycastle作为加密算法实现
//  }
  public static void main(String[] args){
    RsaHelper rsaHelper = new RsaHelper(
        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHvALGmP3G6TwAdM9rlDANyDpv"
            + "fe3j1jlHYJrd6h62vJnC0OdJItB6iFLjylguTHULYahgJUNewEu4qpu7B0NaB3dV"
            + "pxxyaQLnwRjHjYeTpBwoe2ejHhldwO7zACSJt1HhkXKzY5UwkXUokjEEAyxcrPLm"
            + "gc+blIxS/cF45eUOfQIDAQAB",
        "MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIwMaqBFWf6uoCAggA"
            + "MBQGCCqGSIb3DQMHBAipixFamw/b8ASCAoD5A5RrEn3/TaVAYONj8EieBfLT59jq"
            + "gBA44DxGWnt4tBQZjOcRvMOHbXM8JmKfzEi90GBUIDPYJOpfZY6btFvcgfhTXoLc"
            + "Z8YUsuVoHQTgi19R+ykHo0ENN9yYQsQ9OE0FxrsOOxExt8CKUT9Gs5RtXzs5zftg"
            + "QToiUQ38nszpXa2vQrYhJlMZ1CE7uBAQclS0wsN6yyIEy4NjLHOLlrS6hAQBExF5"
            + "mVIg4t3J6Fgz2fFdE/ogeLUxcViwdlM5xNZi8TRR+ZI5Dloj+HsMyFUhFwYfHVwc"
            + "4k5fVo48IfVwUHLMZMCXjhrqL0oVeIYO2YcVzqnNrvezMWE4tAYbxaoWCmvKr/eC"
            + "pS8Y9YDFf2/p2xZgg4pLtqWflkJEjqpH4NfuO6bpU8CI/Fhx+/48FErCCEmdfSZq"
            + "8IILzuoAknmn2v8Jv3cEtnBE3Rq9u9oioAlS2lP5E3jtqR6bHEPPLjYTGbfmR73o"
            + "0i0/rTwuNZcrL71u8Y8GxcgtbvdAECuUL31BO6cn3tyYBJLekT67G1p9dMwx40pC"
            + "8qQ+sNuS9VhLe0jv+eRnhYSQI9uXD3rEFK3cwjdvBJFjGddBNX3VZmp2I4md5aTv"
            + "35dqyX1YCColpEd2aAJWstlXDTxspoSEAd2KXorzdLgJCFcKX1j+pBMvOxrIUWTV"
            + "mkP6ZImZXPQ845J9/up9fQw4E6bcqLCwskpzNFNz1BUzVjjcgzLS3qDCPUmbXF/O"
            + "fioM7HhSV0/Pt5DhLhX0xaWiyPVHMRKkSq6HP8OFfwIuswaxZ7HJrGQjomIOFFip"
            + "aYrF4n+Pi6hqrp4iXbdL4/+SEqMVTB+n3CVOQyiDBnicu3IsCe34npAp");
    byte[] encrypt = rsaHelper.encrypt("hello".getBytes());
    System.out.println(Base64.getEncoder().encodeToString(encrypt));
    byte[] decrypt = rsaHelper.decrypt(encrypt);
    System.out.println(new String(decrypt));
  }
  public RsaHelper(String publicKey, String privateKey) {
    this(Base64.getDecoder().decode(publicKey), Base64.getDecoder().decode(privateKey));
  }

  public RsaHelper(byte[] publicKey, byte[] privateKey) {
    try {
      KeyFactory keyFactory = KeyFactory.getInstance("RSA");
      if (publicKey != null && publicKey.length > 0) {
        this.publicKey = (RSAPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
      }
      if (privateKey != null && privateKey.length > 0) {
        this.privateKey = (RSAPrivateCrtKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey));
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public RsaHelper(String publicKey) {
    this(Base64.getDecoder().decode(publicKey));
  }

  public RsaHelper(byte[] publicKey) {
    try {
      KeyFactory keyFactory = KeyFactory.getInstance("RSA");
      if (publicKey != null && publicKey.length > 0) {
        this.publicKey = (RSAPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public byte[] encrypt(byte[] content) {
    if (publicKey == null) {
      throw new RuntimeException("public key is null.");
    }

    if (content == null) {
      return null;
    }

    try {
      Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
      cipher.init(Cipher.ENCRYPT_MODE, publicKey);
      int size = publicKey.getModulus().bitLength() / 8 - 11;
      ByteArrayOutputStream baos = new ByteArrayOutputStream((content.length + size - 1) / size * (size + 11));
      int left = 0;
      for (int i = 0; i < content.length; ) {
        left = content.length - i;
        if (left > size) {
          cipher.update(content, i, size);
          i += size;
        } else {
          cipher.update(content, i, left);
          i += left;
        }
        baos.write(cipher.doFinal());
      }
      return baos.toByteArray();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public byte[] decrypt(byte[] secret) {
    if (privateKey == null) {
      throw new RuntimeException("private key is null.");
    }

    if (secret == null) {
      return null;
    }

    try {
      Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
      cipher.init(Cipher.DECRYPT_MODE, privateKey);
      int size = privateKey.getModulus().bitLength() / 8;
      ByteArrayOutputStream baos = new ByteArrayOutputStream((secret.length + size - 12) / (size - 11) * size);
      int left = 0;
      for (int i = 0; i < secret.length; ) {
        left = secret.length - i;
        if (left > size) {
          cipher.update(secret, i, size);
          i += size;
        } else {
          cipher.update(secret, i, left);
          i += left;
        }
        baos.write(cipher.doFinal());
      }
      return baos.toByteArray();
    } catch (Exception e) {
      logger.error("rsa decrypt failed.", e);
    }
    return null;
  }

  public byte[] sign(byte[] content) {
    if (privateKey == null) {
      throw new RuntimeException("private key is null.");
    }
    if (content == null) {
      return null;
    }
    try {
      Signature signature = Signature.getInstance("SHA1WithRSA");
      signature.initSign(privateKey);
      signature.update(content);
      return signature.sign();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public boolean verify(byte[] sign, byte[] content) {
    if (publicKey == null) {
      throw new RuntimeException("public key is null.");
    }
    if (sign == null || content == null) {
      return false;
    }
    try {
      Signature signature = Signature.getInstance("SHA1WithRSA");
      signature.initVerify(publicKey);
      signature.update(content);
      return signature.verify(sign);
    } catch (Exception e) {
      logger.error("rsa verify failed.", e);
    }
    return false;
  }
}